package roborally.program.commands;

import be.kuleuven.cs.som.annotate.*;
import roborally.model.Robot;
import roborally.program.conditions.Condition;

/**
 * A class for dealing with selections: (if(condition)(command)(command)).     
 * 
 * @invar	The condition of each selection must be valid condition.
 * 			| canHaveAsCondition(getCondition())
 * @invar	The if-command of each selection must be a valid command.
 * 			| canHaveAsCommand(getFirstCommand())
 * @invar	The else-command of each selection must be valid command.
 * 			| canHaveAsCommand(getSecondCommand())
 * 
 * @version	1.0
 * @author  Pieter Robberechts	(1e Bach Informatica)
 * @author	Jeroen Sanders		(1e Bach Informatica)
 *
 */
public class Selection extends Command {

	/**
	 * Initialize this new selection with a line number, condition and two commands.
	 * 
	 * @param 	condition
	 * 			The condition for executing the if-part.
	 * @param 	firstCommand
	 * 			The command to be executed if the condition evaluates to true. 
	 * @param 	secondCommand
	 * 			The command to be executed if the condition evaluates to false.
	 * @pre		The condition for this selection must be valid.
	 * 			| canHaveAsCondition(condition)
	 * @pre		The if-command for this selection must be valid.
	 * 			| canHaveAsCommand(firstCommand)
	 * @pre		The else-command for this selection must be valid.
	 * 			| canHaveAsCommand(secondCommand)
	 * @post	The condition of this selection is set to the given condition.
	 * 			| new.getCondition() == condition
	 * @post	The if-command of this selection is set to the given command.
	 * 			| new.getFirstCommand() == firstCommand
	 * @post	The else-command of this selection is set to the given command.
	 * 			| new.getSecondCommand() == secondCommand
	 */
	public Selection(int lineNb, Condition condition, 
			Command firstCommand, Command secondCommand) {
		super(lineNb);
		assert(canHaveAsCondition(condition));
		assert(canHaveAsCommand(firstCommand));
		assert(canHaveAsCommand(secondCommand));
		this.condition = condition;
		this.firstCommand = firstCommand;
		this.secondCommand = secondCommand;
	}
	
	/**
	 * Variable registering the condition of this selection.
	 */
	private final Condition condition;
	
	/**
	 * Variable registering the command to be executed if the condition evaluates to true.
	 */
	private final Command firstCommand;
	
	/**
	 * Variable registering the command to be executed if the condition evaluates to false.
	 */
	private final Command secondCommand;
	
	/**
	 * Return the condition associated with this selection.
	 */
	@Basic @Raw @Immutable
	public Condition getCondition() {
		return condition;
	}

	/**
	 * Return the command to be executed if the condition evaluates to true.
	 */
	@Basic @Raw @Immutable
	public Command getFirstCommand() {
		return firstCommand;
	}

	/**
	 * Return the command to be executed if the condition evaluates to false.
	 */
	@Basic @Raw @Immutable
	public Command getSecondCommand() {
		return secondCommand;
	}
	
	/**
	 * Check whether the given condition is a valid one for a selection.
	 * 
	 * @param 	condition
	 * 			The condition to check for whether it is a valid one.
	 * @return	Return true if and only if the given condition is effective;
	 * 			else return false.
	 * 			| result == ( condition != null )
	 */
	@Raw
	public boolean canHaveAsCondition(Condition condition) {
		return ( condition != null);
	}
	
	/**
	 * Check whether the given command is a valid one for a selection.
	 * 
	 * @param 	command
	 * 			The command to check for whether it is a valid one.
	 * @return	Return true if and only if the given command is effective;
	 * 			else return false.
	 * 			| result == ( command != null )
	 */
	@Raw
	public boolean canHaveAsCommand(Command command) {
		return ( command != null);
	}

	/**
	 * Return a textual representation of this selection,
	 * formatted in accordance with its position in the program.
	 * 
	 * @return	The result is a pretty formatted string of this selection,
	 * 			taking in account his relative position in the program.
	 * 			| result == 
	 * 			|	getIndentation(indentationLevel) + "(if\n" + 
	 *			|	getCondition().prettyFormat(indentationLevel+1) + "\n" + 
	 *			|	getFirstCommand().prettyFormat(indentationLevel+1) + "\n" + 
	 *			|	getSecondCommand().prettyFormat(indentationLevel+1) + "\n" +
	 *			|	getIndentation(indentationLevel) + ")"
	 */ 
	@Override
	public String prettyFormat(int indentationLevel) {
		return getIndentation(indentationLevel) + "(if\n" + 
				getCondition().prettyFormat(indentationLevel+1) + "\n" + 
				getFirstCommand().prettyFormat(indentationLevel+1) + "\n" + 
				getSecondCommand().prettyFormat(indentationLevel+1) + "\n" +
				getIndentation(indentationLevel) + ")";
	}

	/**
	 * Return the next basic command for a robot, given the last executed line number. 
	 * 
	 * @return	Return null if the given line number is larger than or equal to
	 * 			the line number of this selection.
	 * 			| if( lineNb >= getLineNb() )
	 * 			| 	the result == null
	 * @return	Search the next basic command in the if-command of this selection and 
	 * 			return the result if the condition of this selection evaluates to true;
	 * 			else search the next basic command in the else-command of this selection
	 * 			and return the result.
	 * 			| if(getCondition().evaluate(robot))
	 *			|	then result == getFirstCommand().getNextBasicCommand(lineNb, robot)
	 *			| else
	 *			|	then result == getSecondCommand().getNextBasicCommand(lineNb, robot);
	 */
	@Override
	public BasicCommand getNextBasicCommand(int lineNb, Robot robot) {
		// The next basic command has to be searched outside of this selection
		if(lineNb >= getLineNb())
			return null;
		
		// The next basic command might be an element of this selection
		if(getCondition().evaluate(robot))
			return getFirstCommand().getNextBasicCommand(lineNb, robot);
		return getSecondCommand().getNextBasicCommand(lineNb, robot);
	}
	
}
